(in-package :leam)

(defclass leam-data-stream-base()
  ((stream-length
    :initform 0
    :initarg :stream-length
    :accessor stream-length
    :documentation "Length of this stream")
   (data-stream
    :initform nil
    :initarg :data-stream
    :accessor data-stream
    :documentation "Stream to perform io")))
(export 'leam-data-stream-base)

(defgeneric setup-data-stream( dstream length stream )
  (:documentation "Set the local parameters on the data stream")
  (:method ((dstream leam-data-stream-base) length stream)
    (with-accessors ((slength stream-length)(sstream data-stream)) dstream
		    (setf slength length)
		    (setf sstream stream))))

(defgeneric get-remaining(stream)
  (:documentation "Get the remaining data in the stream")
  (:method (stream )
    (stream-length stream)))
(export 'get-remaining)

(defgeneric check-length (stream num-bytes)
  (:documentation "Ensure the required length is in the stream")
  (:method (stream num-bytes)
    (with-accessors ((length stream-length)) stream
		    (when (> num-bytes length)
		      (error 'leam-data-stream-length-error))
		    (setf length (- length num-bytes)))))
		    
(defclass leam-data-in-stream(leam-data-stream-base)())
(defclass leam-data-out-stream(leam-data-stream-base)())

(export 'leam-data-in-stream)
(export 'leam-data-out-stream)

(defmethod ignore-data((stream leam-data-in-stream) num-bytes)
    (with-accessors
	((length stream-length)(sstream data-stream))
      stream
      (let ((ignore-len (min num-bytes length)))
	(setf length (- length ignore-len))
	(ignore-data sstream ignore-len))))
(export 'ignore-data)

(defgeneric check-finish(stream)
  (:documentation "Check to see if we should flush the output stream")
  (:method ((stream leam-data-out-stream))
    (with-accessors
	((length stream-length)(sstream data-stream)) stream
	(when (= 0 length)
	  (leam-finish-output sstream)))))

(defmethod read-unsigned-8((stream leam-data-in-stream))
  "Specialization for data streams"
  (check-length stream 1)
  (read-unsigned-8 (data-stream stream)))

(defmethod write-unsigned-8((stream leam-data-out-stream) data)
  "Specialization for data streams"
  (check-length stream 1)
  (write-unsigned-8 (data-stream stream) data)
  (check-finish stream))

(defmethod read-byte-data((stream leam-data-in-stream) sequence)
  "Specialization for read-byte-data"
  (check-length stream (length sequence))
  (read-byte-data (data-stream stream) sequence))

(defmethod write-byte-data((stream leam-data-out-stream) sequence)
  "Specialization for write-byte-data"
  (let ((seq-len (length sequence)))
    (check-length stream seq-len )
    (write-byte-data (data-stream stream) sequence)
    (check-finish stream)))
